package org.myspringframework.aop.framework;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.myspringframework.aop.AdvisedSupport;
import org.myspringframework.aop.Advisor;

import java.lang.reflect.Method;
import java.util.List;

//使用cglib生成的aop代理类
public class Cglib2AopProxy implements AopProxy {

    private final AdvisedSupport advised;

    public Cglib2AopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }

    @Override
    public Object getProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(advised.getTargetSource().getTarget().getClass());
        enhancer.setInterfaces(advised.getTargetSource().getTargetClass());
        enhancer.setCallback(new DynamicAdvisedInterceptor(advised));
        return enhancer.create();
    }

    private static class DynamicAdvisedInterceptor implements MethodInterceptor {

        private final AdvisedSupport advised;

        public DynamicAdvisedInterceptor(AdvisedSupport advised) {
            this.advised = advised;
        }

        //代理类的所有方法都会被这个方法拦截
        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            List<Object> chain = advised.getInterceptorsAndDynamicInterceptionAdvice(method, advised.getTargetSource().getTarget().getClass());
            CglibMethodInvocation methodInvocation = new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, args, chain,methodProxy);
            if (chain==null||chain.size()==0){
                methodInvocation.invokeJoinpoint();
            }else{
                //有匹配的拦截器链
                return methodInvocation.proceed();
            }
            return methodInvocation.invokeJoinpoint();
        }
    }

    private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

        private final MethodProxy methodProxy;

        public CglibMethodInvocation(Object target, Method method, Object[] arguments, List<Object> chain, MethodProxy methodProxy) {
            super(target, method, arguments,chain);
            this.methodProxy = methodProxy;
        }

        //重写调用原目标方法的逻辑（即不使用反射）
        @Override
        protected Object invokeJoinpoint() throws Throwable {
            return this.methodProxy.invoke(this.target, this.arguments);
        }
    }

}